﻿namespace MicroCloud.Events
{
    /// <summary>
    /// 实体条目事件处理器
    /// </summary>
    public class EntityEntryEventHandler : EventHandlerBase<EntityEntryEventData>
    {
        #region "属性"
        // 受保护属性
        #region "获取 服务提供者对象"
        /// <summary>
        /// 获取 服务提供者对象
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }
        #endregion
        #region "获取 日志对象"
        /// <summary>
        /// 获取 日志对象
        /// </summary>
        protected ILogger Logger => ServiceProvider.GetLogger(GetType());
        #endregion
        #region "获取 数据库存储过程处理器集合"
        /// <summary>
        /// 获取 数据库存储过程处理器集合
        /// </summary>
        protected IEnumerable<IProcedureHandler> ProcedureHandlers => ServiceProvider.GetServices<IProcedureHandler>();
        #endregion

        #endregion

        #region "构造函数"
        #region "初始化一个实体条目事件处理器的新实例"
        /// <summary>
        /// 初始化一个实体条目事件处理器 <see cref="EntityEntryEventHandler"/> 的新实例
        /// </summary>
        /// <param name="provider">服务提供者</param>
        public EntityEntryEventHandler(IServiceProvider provider)
        {
            ServiceProvider = provider;
        }
        #endregion

        #endregion

        #region "方法"
        #region "事件处理"
        /// <summary>
        /// 事件处理
        /// </summary>
        /// <param name="eventData">事件源数据</param>
        /// <param name="provider">服务提供者</param>
        public override void Handle(EntityEntryEventData eventData, IServiceProvider provider)
        {
            var dbContext = eventData.DbContext;
            var dbContextType = dbContext.GetType();
            var spTransactionNotification = ProcedureHandlers?.Where(m => m.DbContextOptions.Any(o => o.Key == dbContextType) && m.GetType() == typeof(SP_TransactionNotification)).FirstOrDefault();
            if (spTransactionNotification == null)
            {
                return;
            }
            var databaseType = spTransactionNotification.DbContextOptions[dbContextType].DatabaseType;
            var spName = spTransactionNotification.GetProcedureName(dbContext);
            if (spName.IsMissing())
            {
                return;
            }
            var entityEntries = eventData.EntityEntries;
            foreach (var entry in entityEntries)
            {
                var entity_name = entry.EntityName;
                var entity_type_name = entry.EntityTypeName;
                var operate_type = entry.OperateType;
                var db_schema = entry.DbSchema;
                var db_object_name = entry.DbObjName;
                var db_object_type = entry.DbObjType;
                var db_object_key_field = entry.KeyField;
                var db_object_key_value = entry.KeyValue;
                var db_object_content = entry.Entity.ToJsonString().Replace("'", "''");

                var executeSql = string.Empty;
                switch (databaseType)
                {
                    case DatabaseType.Sqlite:
                        break;
                    case DatabaseType.MySql:
                        executeSql = $"CALL {spName}('{entity_name}','{entity_type_name}',{(int)operate_type},'{db_schema}','{db_object_name}',{(int)db_object_type},'{db_object_key_field}','{db_object_key_value}','{db_object_content}');";
                        break;
                    case DatabaseType.SqlServer:
                        executeSql = $"EXEC {spName} '{entity_name}','{entity_type_name}',{(int)operate_type},'{db_schema}','{db_object_name}',{(int)db_object_type},'{db_object_key_field}','{db_object_key_value}','{db_object_content}'";
                        break;
                    case DatabaseType.PostgreSql:
                        executeSql = $"call {spName}('{entity_name}','{entity_type_name}',{(int)operate_type},'{db_schema}','{db_object_name}',{(int)db_object_type},'{db_object_key_field}','{db_object_key_value}','{db_object_content}');";
                        break;
                    case DatabaseType.Oracle:
                        executeSql = $"CALL {spName}('{entity_name}','{entity_type_name}',{(int)operate_type},'{db_schema}','{db_object_name}',{(int)db_object_type},'{db_object_key_field}','{db_object_key_value}','{db_object_content}');";
                        break;
                }
                if (!executeSql.IsMissing())
                {
                    var result = dbContext.SqlQueryToDataTable(executeSql);
                    if (result == null)
                    {
                        Logger.LogError("实体事件处理器执行SQL语句“{executeSql}”未获取到返回结果。", executeSql);
                        throw new Exception(I18N.T("实体事件调用存储过程 {0} 出错：未获取到返回结果。", spName));
                    }
                    if (result.Rows.Count <= 0)
                    {
                        Logger.LogError("实体事件处理器执行SQL语句“{executeSql}”返回的结果未包含任何数据。", executeSql);
                        throw new Exception(I18N.T("实体事件调用存储过程 {0} 出错：返回的结果未包含任何数据。", spName));
                    }
                    if (result.Columns.Count <= 0)
                    {
                        Logger.LogError("实体事件处理器执行SQL语句“{executeSql}”返回的结果未包含列信息。", executeSql);
                        throw new Exception(I18N.T("实体事件调用存储过程 {0} 出错：返回的结果未包含列信息。", spName));
                    }
                    var firstRow = result.Rows[0];
                    var codeValueType = firstRow[0].GetType();
                    if (!Equals(codeValueType, typeof(int)))
                    {
                        Logger.LogError("实体事件处理器执行SQL语句“{executeSql}”，返回的结果第一列类型必须为整数，而当前返回类型为 {codeValueType}。", executeSql, codeValueType);
                        throw new Exception(I18N.T("实体事件调用存储过程 {0} 出错：返回第一列类型必须为整数。", spName));
                    }
                    var messageValueType = firstRow[1].GetType();
                    if (!Equals(messageValueType, typeof(string)))
                    {
                        Logger.LogError("实体事件处理器执行SQL语句“{executeSql}”，返回的结果第二列类型必须为字符，而当前返回类型为 {messageValueType}。", executeSql, messageValueType);
                        throw new Exception(I18N.T("实体事件调用存储过程 {0} 出错：返回第二列类型必须为字符。", spName));
                    }

                    var codeValue = firstRow[0].CastTo<int>();
                    var messageValue = firstRow[1].CastTo<string>();
                    var errorMessage = $"{codeValue}";
                    if (!messageValue.IsMissing())
                    {
                        errorMessage = $"{errorMessage} - {messageValue}";
                    }
                    if (codeValue != 0)
                    {
                        throw new Exception(errorMessage);
                    }
                    //Logger.LogDebug("实体事件处理器执行SQL语句“{executeSql}”完成",executeSql);   //当SQL语句过大时，占用日志体积太大，故注释。
                }
            }
        }
        #endregion

        #endregion

    }
}